home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Suzy B Software 2
/
Suzy B Software CD-ROM 2 (1994).iso
/
adult_ed
/
grapher
/
graph
/
grapher.pas
next >
Wrap
Pascal/Delphi Source File
|
1995-05-02
|
60KB
|
1,783 lines
program Grapher(input, output);
const
Pi = 3.14159265;
ClrScr = 69; {Clear screen and home the cursor}
Cursor_on = 101; {Turn blinking cursor on}
Cursor_off = 102; {Turn blinking cursor off}
{$I GEMCONST.PAS}
type
Str7 = string[7];
Draw_Type = (Redraw, No_Grid, Old_Grid);
Grid_Type = (Rectangular, Polar, Trigonometric);
Res_Type = (Low, Med, Hi);
TokenType = (Numeric, Character);
NodePtr = ^Node;
Node = record
Link: NodePtr;
case NodeType: TokenType of
Numeric: (Value: real);
Character: (Code: char;
Priority: 0..5)
end;
Screen_Type = packed array [0..31999] of byte;
Screen_Ptr = ^Screen_Type;
{$I GEMTYPE.PAS}
var
InFix: Str255; {Infix expression}
TempPtr, {Temporary pointer}
PostFix: NodePtr; {Postfix expression}
X_Scale, {Scale on X-axis}
Y_Scale: real; {Scale used on graph grid}
Color: integer; {Color of graph}
Draw: Draw_Type; {Grid drawing option}
Grid: Grid_Type; {Type of grid}
Event: integer; {Holds Get_Event value}
DummyMsg: Message_Buffer; {Dummy variable used in Get_Event call}
D: integer; {Dummy variable used in Get_Event call}
{The following variables are accessed as global variables}
Res: Res_Type; {Screen resolution}
X_Pix, {Number of pixels in horizontal direction}
Y_Pix, {Number of pixels in vertical direction}
X_Center, {Center of screen horizontally}
Y_Center, {Center of screen vertically}
Num_Color: integer; {Number of colors available for graph}
SF: real; {Scaling factor need to compensate for screen
aspect ratio}
Display_Area, {Pointer to display area in memory}
Temp_Screen: Screen_Ptr;{Used to save a graph display}
GEM_Interface: boolean; {TRUE if user selects GEM-based interface}
GridStr, {This and next four are used to store user}
DrawStr, {input for grid type, grid drawing option,}
ColorStr, {graph color, }
XStr, {X-scale, and }
YStr: Str255; {Y-scale. }
{$I GEMSUBS.PAS}
{**************** Out_Escape ******************
* *
* Send escape codes that control the cursor. *
* *
* Called by: Initialization, *
* Get_Expression, *
* Get_Graph_Parameters_OK *
* *
* In parameter: Ascii code of the character *
* following escape code *
************************************************}
procedure Out_Escape(c : integer);
procedure bconout(device, c:integer);
BIOS(3);
begin
bconout(2, 27); {The escape character}
bconout(2, c)
end; {Out_Escape}
{************* Initialization *****************
* *
* Sets the value of various global screen *
* parameters based on the screen resolution. *
* Also sets initial default values of graph *
* parameters: grid scales, color, and *
* expression to be graphed. *
* *
* Called by: MAIN DRIVER *
* *
* All variables used in this procedure are *
* accessed as globals *
************************************************}
procedure Initialization;
var
AlertStr: Str255; {Alert box string}
Blanks: string[25]; {A string of blanks}
Num: integer; {Dummy value used in Alert Box call}
Scr_Res: integer; {Holds screen resolution value}
Dummy: char;
{------------- Physical Screen -------------
| |
| Returns pointer to physical screen area. |
| |
| Called by: MAIN DRIVER |
---------------------------------------------}
function Physical_Screen: Screen_Ptr;
XBIOS(2);
{------------- Get Resolution --------------
| |
| Returns 0, 1, or 2 indicating current |
| screen resolution low, med, or high. |
| |
| Called by: MAIN DRIVER |
---------------------------------------------}
function Get_Res : Integer;
XBIOS(4);
begin
{ Set up screen parameters based on display resolution. }
Scr_Res := Get_Res;
case Scr_Res of
0 : begin
Res := Low;
X_Pix := 320;
Y_Pix := 200;
SF := 0.869;
Num_Color := 15
end; {0}
1 : begin
Res := Med;
X_Pix := 640;
Y_Pix := 200;
SF := 0.434;
Num_Color := 3
end; {1}
2 : begin
Res := Hi;
X_Pix := 640;
Y_Pix := 400;
SF := 0.869;
Num_Color := 1
end; {3}
end; {case}
{ Print Copyright Message }
Out_Escape(ClrScr);
if Res = Low then
Blanks := ' '
else
Blanks := ' ';
writeln(Blanks,' Grapher');
writeln(Blanks,' by Delmar Searls');
writeln;
writeln(Blanks,' (Parts of this product are');
writeln(Blanks,'Copyright (c) 1986, OSS & CCD');
writeln(Blanks,' Used by permission of OSS)');
{ Let user select type of interface }
AlertStr := '[2]';
AlertStr := concat(AlertStr, '[ Choose a |');
AlertStr := concat(AlertStr, ' text-oriented |');
AlertStr := concat(AlertStr, ' or GEM-based |');
AlertStr := concat(AlertStr, ' interface. ]');
AlertStr := concat(AlertStr, '[ Text | GEM ]');
Num := Do_Alert(AlertStr, 0);
if Num = 1 then
GEM_Interface := FALSE
else
GEM_Interface := TRUE;
Out_Escape(ClrScr);
{ Set clipping boundaries and find coordinates of center of display. }
Set_Clip(0,0,X_Pix,Y_Pix);
X_Center := X_Pix DIV 2;
Y_Center := Y_Pix DIV 2;
{ Set up initial default values }
GridStr := 'R';
DrawStr := '1';
ColorStr := '1'; Color := 1;
XStr := '1'; X_Scale := 1;
YStr := '1'; Y_Scale := 1;
InFix := 'SIN(X)';
Display_Area := Physical_Screen;
new(Temp_Screen)
end; {Initialization)
{**************** Str_to_Num ******************
* *
* Converts a string representation of a number *
* to the numeric representation. *
* *
* Called by: Next_Token, Get_Scale, *
* Get_Graph_Parameters_OK *
* *
* In parameter: The string representation *
* Out parameter: Syntax error flag *
************************************************}
function Str_to_Num(NumStr {in}: Str255;
var Syntax_Error {out}: boolean): Real;
var
Integer_Part, {Integer part of number}
Fraction_Part, {Fraction part of number}
Power_of_Ten: real; {Used in finding fraction part}
DP, {Position of decimal point}
Num_Int_Digits, {Number of digits in integer part}
Num_Frac_Digits, {Number of digits in fractional part}
I: integer; {Loop counter}
begin
{ Initialize variables. }
Integer_Part := 0;
Fraction_Part := 0;
Power_of_Ten := 1;
{ Determine number of digits in integer part and fraction part. }
DP := pos('.', NumStr);
if DP = 0 then begin { string represents an integer }
Num_Int_Digits := length(NumStr);
Num_Frac_Digits := 0
end {if}
else begin { string represents a real }
Num_Int_Digits := DP-1;
Num_Frac_Digits := length(NumStr)-DP
end; {else}
{ Convert integer part to numeric form. }
for I := 1 to Num_Int_Digits do begin
Integer_Part := 10*Integer_Part + ord(NumStr[1]) - ord('0');
delete(NumStr,1,1)
end; {for}
if NumStr <> '' then { delete decimal point from string }
delete(NumStr,1,1);
{ Convert fraction part (if any) to numeric form. }
if Num_Frac_Digits > 0 then { first check for extra decimal point }
if pos('.', NumStr) = 0 then begin { conversion process }
for I := 1 to Num_Frac_Digits do begin
Fraction_Part := 10*Fraction_Part + ord(NumStr[1]) - ord('0');
Power_of_Ten := 10*Power_of_Ten;
delete(NumStr,1,1)
end; {for}
Fraction_Part := Fraction_Part/Power_of_Ten
end {if}
else
Syntax_Error := TRUE;
Str_to_Num := Integer_Part + Fraction_Part
end; {Str_to_Num}
{************** Convert ***********************
* *
* This function converts the input expression *
* from infix to postfix notation. A pointer *
* to the postfix expression is returned as the *
* value of Convert. *
* *
* Called by: Get_Expression *
* *
* In parameter: The infix expression *
* Out parameter: Syntax error flag *
************************************************}
function Convert(InString {in}: Str255;
var Syntax_Error {out}: boolean): NodePtr;
var
TempStr: Str255; {Temporary storage of Infix expression}
PostFix, {Pointer to the postfix expression}
Tail, {Pointer to last token in postfix expression}
Token, {A token to be added to postfix expression}
TOS: NodePtr; {Pointer to top of stack used in conversion}
I, {Loop counter}
L: integer; {Length of InFix expression}
Previous_Token: char; {Denotes the type of the previous token. This
has a value of '(' for right parenthesis, and
a 'N' if previous token was numeric. Numeric
tokens are numbers, 'X', and ')'. A code of
'F' indicates a function token. Otherwise
this identifier is assigned the null character. }
{------------ Next_Token -------------------
| |
| This function removes the next item from |
| the infix expression and returns the |
| corresponding token. |
| |
| Called by: Convert |
| |
| In/Out parameter: The infix expression |
| Previous token |
| Out parameter: Syntax error flag |
---------------------------------------------}
function Next_Token(var InFix {in/out}: Str255;
var Previous_Token: char;
var Syntax_Error {out}: boolean): NodePtr;
var
Token: NodePtr; {The new token}
TStr: Str255; {Stores numeric operand in string form}
TChar: char; {Token code for non-numeric tokens}
T: integer; {Temporary storage for token priority}
begin
{ Get and initialize token node. }
new(Token);
Token^.Link := NIL;
while InFix[1] = ' ' do { remove leading blanks }
delete(InFix,1,1);
TStr := InFix[1]; { Transfer first character of infix to TStr. }
delete(InFix,1,1);
if TStr[1] in ['0'..'9','.'] then begin { Token is a number. }
Token^.NodeType := Numeric;
{ Read the number as a string of valid numeric characters. }
while (InFix <> '') and (InFix[1] in ['.','0'..'9']) do begin
TStr := concat(TStr, InFix[1]);
delete(InFix,1,1)
end; {while}
{ Convert string representation to numeric. }
Token^.Value := Str_to_Num(TStr, Syntax_Error);
{ Do a little error checking. A number cannot directly follow
another numeric token or a function token. }
if NOT Syntax_Error then
if (Previous_Token = 'N') OR (Previous_Token = 'F') then
Syntax_Error := TRUE
else {reset previous token code}
Previous_Token := 'N'
end {if}
else begin { Token is character type token. }
Token^.NodeType := Character;
TChar := TStr[1];
Token^.Code := TChar;
{ Determine priority of token }
case TChar of
'X','(',')': Token^.Priority := 0;
'+': Token^.Priority := 1;
'-': if Previous_Token = '(' then begin
Token^.Priority := 3;
TChar := '~';
Token^.Code := '~'
end {if}
else
Token^.Priority := 1;
'*','/': Token^.Priority := 2;
'^': Token^.Priority := 4;
{ Also check for syntax errors in function tokens. }
'A': if (Length(InFix) > 1) and (InFix[1] = 'B')
and (InFix[2] = 'S') then begin
Token^.Priority := 5;
delete(InFix,1,2)
end {if}
else
Syntax_Error := TRUE;
'C': if (Length(InFix) > 1) and (InFix[1] = 'O')
and (InFix[2] = 'S') then begin
Token^.Priority := 5;
delete(InFix,1,2)
end {if}
else
Syntax_Error := TRUE;
'E': if (Length(InFix) > 1) and (InFix[1] = 'X')
and (InFix[2] = 'P') then begin
Token^.Priority := 5;
delete(InFix,1,2)
end {if}
else
Syntax_Error := TRUE;
'L': if (Length(InFix) > 0) and (InFix[1] = 'N') then begin
Token^.Priority := 5;
delete(InFix,1,1)
end {if}
else
Syntax_Error := TRUE;
'S': if (Length(InFix) > 1) and (InFix[1] = 'I')
and (InFix[2] = 'N') then begin
Token^.Priority := 5;
delete(InFix,1,2)
end {if}
else if (Length(Infix)>1) and (Infix[1] = 'Q')
and (Infix[2] = 'R') then begin
Token^.Priority := 5;
Token^.Code := 'R';
delete(InFix,1,2)
end {else if}
else
Syntax_Error := TRUE;
'T': if (Length(Infix) > 1) and (InFix[1] = 'A')
and (InFix[2] = 'N') then begin
Token^.Priority := 5;
delete(InFix,1,2)
end {if}
else
Syntax_Error := TRUE;
OTHERWISE: Syntax_Error := TRUE { Since token was not in list }
end; {case}
if NOT Syntax_Error then begin
{ Do a little error checking. }
T := Token^.Priority;
if ((T = 5) OR (TChar = 'X') OR (TChar = '('))
AND (Previous_Token = 'N') then
Syntax_Error := TRUE
else if ((T = 5) OR (TChar = 'X'))
AND (Previous_Token = 'F') then
Syntax_Error := TRUE
else if ((T = 1) OR (T = 2) OR (T = 4) OR (TChar = ')'))
AND (Previous_Token <> 'N') then
Syntax_Error := TRUE;
{ Reset previous token code. }
if NOT Syntax_Error then
if Token^.Nodetype = Numeric then
Previous_Token := 'N'
else if TChar in ['X',')'] then
Previous_Token := 'N'
else if TChar = '(' then
Previous_Token := '('
else if T = 5 then
Previous_Token := 'F'
else
Previous_Token := chr(0)
end {if}
end; {else}
Next_Token := Token
end; {Next_Token}
{------------------ Append -----------------
| |
| This procedure appends the input token to |
| the postfix expression. |
| |
| Called by: Convert |
| |
| In parameter: The token |
| In/Out parameter: Pointer to last token |
| in postfix expression |
---------------------------------------------}
procedure Append(var Tail {in/out}: NodePtr;
Item {in}: NodePtr);
var Temp: NodePtr;
begin
if Item^.Link <> NIL then {Item is on stack, append copy to postfix. }
new(Temp)
else { The item itself is appended to postfix. }
Temp := Item;
Temp^ := Item^;
Tail^.Link := Temp;
Tail := Temp;
Temp^.Link := NIL
end; {Append}
{----------------- Push --------------------
| |
| Push a token onto the stack |
| |
| Called by: Convert |
| |
| In parameter: The token |
| In/Out parameter: The top of stack ptr |
---------------------------------------------}
procedure Push(var TOS {in/out}: NodePtr;
Item {in}: NodePtr);
begin
Item^.Link := TOS;
TOS := Item
end;
{------------------- Pop --------------------
| |
| Delete the top element from the stack. |
| |
| Called by: Convert |
| |
| In/Out parameter: The top of stack ptr |
---------------------------------------------}
procedure Pop(var TOS {in/out}: NodePtr);
var
Temp: NodePtr;
begin
Temp := TOS;
TOS := TOS^.Link;
dispose(Temp)
end; {Pop}
{******** Convert code starts here *******}
begin
TempStr := InString;
Syntax_Error := FALSE;
Previous_Token := '(';
{ Create 'NULL' node on stack. }
new(TOS);
TOS^.NodeType := Character;
TOS^.Priority := 0;
TOS^.Code := '@';
TOS^.Link := NIL;
{Create a dummy head node. }
new(PostFix);
Tail := PostFix;
{ Process the user's infix expression. }
while (Length(InString) > 0) and not Syntax_Error do begin
Token := Next_Token(InString, Previous_Token, Syntax_Error);
if not Syntax_Error then begin
{ Numbers and variable X are immediately appended to postfix. }
if Token^.NodeType = Numeric then
Append(Tail, Token)
else if Token^.Code = 'X' then
Append(Tail, Token)
{ Left parenthesis is pushed onto the stack. }
else if Token^.Code = '(' then
Push(TOS, Token)
{ When a right parenthesis is encountered, operators are pulled
from the stack and appended to postfix until the corresponding
left parenthesis is encountered. The left parenthesis is
pulled from the stack, and both parentheses are discarded. }
else if Token^.Code = ')' then begin
while (TOS^.Code <> '(') and (TOS^.Code <> '@') do begin
Append(Tail, TOS);
Pop(TOS)
end; {while}
if TOS^.Code = '@' then
Syntax_Error := TRUE
else
Pop(TOS)
end {else if}
{ The only thing left is operators. Operators of higher priority,
if any, are pulled from the stack and appended to postfix. The
current operator is then pushed onto the stack. }
else begin
while Token^.Priority <= TOS^.Priority do begin
Append(Tail, TOS);
Pop(TOS)
end; {while}
Push(TOS, Token)
end {else}
end {if}
end; {while}
if Syntax_Error then begin { Print syntax error message if needed. }
if GEM_Interface then begin
Out_Escape(ClrScr);
writeln('Y = ',TempStr)
end; {if}
L := length(TempStr) - length(InString) + 4;
for I := 1 to L do
write(' ');
writeln('^');
writeln('Syntax error!')
end {if}
{ Remove the remaining operators from the stack and append to postfix. }
else begin
while TOS^.Code <> '@' do begin
if TOS^.Code = '(' then begin
Syntax_Error := TRUE;
if GEM_Interface then
Out_Escape(ClrScr);
writeln('Unmatched Left Parenthesis!')
end;
Append(Tail, TOS);
Pop(TOS)
end; {while}
Pop(TOS) { Pull NULL node from stack }
end; {else}
Convert := PostFix^.Link;
dispose(PostFix)
end; {Convert}
{**************** Evaluate ********************
* *
* Evaluates the Postfix expression for the *
* value of X passed to it. *
* *
* Called by: Get_Expression, Rect_Graph, *
* and Polar_Graph *
* *
* In parameters: The postfix expression and *
* The value of X *
* Out parameters: Postfix error flag and *
* Undefined result flag *
************************************************}
function Evaluate(Head {in}: NodePtr;
X {in}: real;
var PostFix_Error {out},
Undefined {out}: boolean): real;
var
TOS: 0..100;
Stack: array [1..100] of real;
Cosine_Val: real;
Temp: integer;
begin
{ Initialize flags and data stack. }
PostFix_Error := FALSE;
Undefined := FALSE;
TOS := 0;
{ Process postfix expression }
while (Head <> NIL) and not PostFix_Error and not Undefined do begin
{ Push numbers onto the stack, }
if Head^.NodeType = Numeric then begin
TOS := TOS + 1;
Stack[TOS] := Head^.Value
end {if}
{ or push the value of the variable onto the stack, }
else if Head^.Code = 'X' then begin
TOS := TOS + 1;
Stack[TOS] := X
end {else if}
{ or apply negation operator, }
else if Head^.Priority = 3 then
if TOS>0 then
Stack[TOS] := -Stack[TOS]
else
PostFix_Error := TRUE
{ or apply function to TOS element, }
else if Head^.Priority = 5 then
if TOS>0 then
case Head^.Code of
'A': Stack[TOS] := ABS(Stack[TOS]);
'C': Stack[TOS] := COS(Stack[TOS]);
'E': if Stack[TOS] < -50 then
Stack[TOS] := 0
else if Stack[TOS] < 50 then
Stack[TOS] := EXP(Stack[TOS])
else
Undefined := TRUE;
'L': if Stack[TOS] > 0 then
Stack[TOS] := LN(Stack[TOS])
else
Undefined := TRUE;
'R': if Stack[TOS] >= 0 then
Stack[TOS] := SQRT(Stack[TOS])
else
Undefined := TRUE;
'S': Stack[TOS] := SIN(Stack[TOS]);
'T': begin
Cosine_Val := COS(Stack[TOS]);
if ABS(Cosine_Val) > 0.000001 then
Stack[TOS] := SIN(Stack[TOS])/COS(Stack[TOS])
else
Undefined := TRUE
end {case option}
end {case}
else
PostFix_Error := TRUE
{ or else the token is a binary operator which is applied to top
two stack elements and the result replaces both of them. }
else if TOS>1 then begin
TOS := TOS - 1;
case Head^.Code of
'+': Stack[TOS] := Stack[TOS] + Stack[TOS+1];
'-': Stack[TOS] := Stack[TOS] - Stack[TOS+1];
'*': Stack[TOS] := Stack[TOS] * Stack[TOS+1];
'/': if ABS(Stack[TOS+1]) > 0.000001 then
Stack[TOS] := Stack[TOS] / Stack[TOS+1]
else
Undefined := TRUE;
{ The program can handle two types of exponentiation. If the
base (TOS) is positive, the normal process of using EXP and LN
functions is used. If the base is negative and the exponent
is an integer, then we have to apply some algebraic trickery
first. If the base has a value of zero, the result is set
to zero as well. }
'^': if Stack[TOS] > 0 then
Stack[TOS] := EXP(Stack[TOS+1]*LN(Stack[TOS]))
else if Stack[TOS] < 0 then begin
Temp := round(Stack[TOS+1]);
if abs(Temp - Stack[TOS+1]) < 0.000001 then begin
Stack[TOS] := EXP(Stack[TOS+1]*LN(-Stack[TOS]));
if Odd(Temp) then
Stack[TOS] := -Stack[TOS]
end {if}
else
Undefined := TRUE
end {else if}
else
Stack[TOS] := 0
end {case}
end {if}
{ If we get this far, then postfix token is invalid. Not likely to
happen. }
else
PostFix_Error := TRUE;
Head := Head^.Link { Move to next token in postfix. }
end; {while}
{ At the end, there should be only one element remaining on the stack,
namely the final result. Otherwise, the postfix expression is invalid.
We skip this if the function is undefined for the current value of X. }
if not Undefined then begin
if TOS = 1 then
Evaluate := Stack[TOS]
else
PostFix_Error := TRUE;
{ Print error message if necessary. }
if PostFix_Error then begin
if GEM_Interface then
Out_Escape(ClrScr);
writeln('Postfix error detected!');
writeln;
writeln('This is usually caused by too few');
writeln('operators. Check for missing arithmetic');
writeln('symbols; especially multiplication "*".')
end {if}
end {if}
end; {Evaluate}
{************** Get Expression ****************
* *
* This procedure asks the user to enter the *
* expression to be graphed. It is entered in *
* normal infix notation and converted to *
* postfix. *
* *
* Called by: MAIN DRIVER *
* *
* Out Parameter: The postfix expression *
* In/Out parameter: The infix expression *
************************************************}
procedure Get_Expression(var InFix {in/out}: Str255;
var PostFix {out}: NodePtr);
var
J, {Loop counter}
Last: integer; {Index of last character in infix expression}
Temp: real; {Used in checking for postfix errors}
TempStr: Str255; {Temporary storage of infix expression}
Dummy,
Syntax_Error, {TRUE if error found during conversion to postfix}
PostFix_Error: boolean; {TRUE if error found during evaluation}
Dialog: Dialog_Ptr; {Pointer to dialog box}
Pushed, {Stores way in which user exited dialog box}
Prompt, {Points to prompt in dialog box}
User_Input, {Points to user input item in dialog box}
Quit_Btn, {Quit button in dialog box}
Ok_Btn: integer; {Ok button in dialog box}
begin
if NOT Gem_Interface then begin
{ Print the instructions and the default infix expression. }
Out_Escape(ClrScr);
Out_Escape(Cursor_On);
writeln;
writeln('Enter the expression you want graphed.');
writeln('(Enter "Q" to QUIT)');
writeln;
writeln('Y = ',InFix);
end; {if}
{ Get a valid infix expression from the user. }
repeat
if GEM_Interface then begin
if Res = Low then
Dialog := New_Dialog(4,0,0,38,5)
else
Dialog := New_Dialog(4,0,0,78,5);
Prompt := Add_DItem(Dialog,G_Text,None,1,1,2,1,0,256*Black);
Set_DText(Dialog,Prompt,'Y=',3,TE_Center);
if Res = Low then begin
User_Input := Add_DItem(Dialog,G_FText,Editable,
3,1,34,1,0,256*Black|128);
Set_DEdit(Dialog,User_Input,'__________________________________',
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
Infix,3,TE_Left)
end {if}
else begin
User_Input := Add_DItem(Dialog,G_FText,Editable,
3,1,74,1,0,256*Black|128);
Set_DEdit(Dialog,User_Input,
'__________________________________________________________________________',
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
Infix,3,TE_Left)
end; {else}
Quit_Btn := Add_DItem(Dialog,G_Button,Selectable|Exit_Btn,
1,3,6,1,0,0);
Set_DText(Dialog,Quit_Btn,'QUIT',3,TE_Center);
Ok_Btn := Add_DItem(Dialog,G_Button,Selectable|Exit_Btn,
9,3,6,1,0,0);
Set_DText(Dialog,Ok_Btn,' OK ',3,TE_Center);
Center_Dialog(Dialog);
Show_Mouse;
Pushed := Do_Dialog(Dialog,User_Input);
End_Dialog(Dialog);
Hide_Mouse;
Out_Escape(ClrScr);
Delete_Dialog(Dialog);
if Pushed = Quit_Btn then
TempStr := 'Q'
else
Get_DEdit(Dialog,User_Input,TempStr)
end {if}
else begin
writeln;
write('Y = ');
readln(TempStr)
end; {else}
Syntax_Error := FALSE;
PostFix_Error := FALSE;
if TempStr <> '' then begin
{Remove trailing blanks}
InFix := TempStr;
Last := Length(InFix);
while (Last > 0) and (InFix[Last] = ' ') do begin
delete(InFix,Last,1);
Last := Last - 1
end; {while}
{Convert to all uppercase}
for J := 1 to Last do
if InFix[J] in ['a'..'z'] then
InFix[J] := chr(ord(InFix[J])-32)
end; {if}
If InFix <> 'Q' then begin { Convert infix to postfix. }
PostFix := Convert(InFix, Syntax_Error);
if NOT Syntax_Error then { Check for postfix error. }
Temp := Evaluate(PostFix, 1.1, PostFix_Error, Dummy)
end {if}
until NOT(Syntax_Error or PostFix_Error);
if NOT GEM_Interface then
Out_Escape(Cursor_Off)
end; {Get}
{********** Get Graph Parameters OK ***********
* *
* Get the grid scale, the color of the graph, *
* and the desired grid drawing option. *
* *
* Called by: MAIN DRIVER *
* *
* Out parameters: Grid type, X & Y scales, *
* color, and grid drawing option. *
* *
* Global variables accessed: Res, Num_Color *
************************************************}
function Get_Graph_Parameters_OK(var Grid {out}: Grid_Type;
var X_Scale {out}: real;
var Y_Scale {out}: real;
var Color {out}: integer;
var Draw {out}: Draw_Type): boolean;
var
I: integer; {Loop counter}
TempStr: Str255; {Temporary string representation}
Temp: real; {Temporary numeric representation}
Syntax_Error: boolean; {True if entry is invalid}
Dialog: Dialog_Ptr; {Pointer to dialog box}
Button: array [1..15] of integer; {Dialog box buttons}
Pushed: integer; {Indicates which button was selected}
Prompt: integer; {Pointer to prompt in dialog box}
{-------------- Select Color ---------------
| |
| Select color for graph line. |
| |
| Called by: Get_Graph_Parameters_OK |
| |
---------------------------------------------}
procedure Select_Color;
var
I : integer; {Loop counter}
begin
if GEM_Interface then begin
Dialog := New_Dialog(16,0,0,8,18);
Prompt := Add_DItem(Dialog,G_Text,None,1,1,6,1,0,256*Black);
Set_DText(Dialog,Prompt,'Color?',3,TE_Center);
for I := 1 to Num_Color do
Button[I] := Add_DItem(Dialog,G_Box,Selectable|Exit_Btn,
2,I+1,4,1,0,112+I);
Center_Dialog(Dialog);
Show_Mouse;
Pushed := Do_Dialog(Dialog,0);
End_Dialog(Dialog);
Hide_Mouse;
Out_Escape(ClrScr);
Delete_Dialog(Dialog);
for I := 1 to Num_Color do
if Pushed = Button[I] then
Color := I
end {if}
else begin
Out_Escape(ClrScr);
for I := 1 to Num_Color do begin
Line_Color(I);
writeln(I:2,' ');
line(24, 8*I-4, 300, 8*I-4)
end; {for}
writeln;
repeat
Syntax_Error := FALSE;
write('Graph color <',ColorStr,'>: '); readln(TempStr);
if TempStr <> '' then begin
for I := 1 to length(TempStr) do
if not (TempStr[I] in ['0'..'9']) then
Syntax_Error := TRUE;
if not Syntax_Error then begin
Temp := Str_to_Num(TempStr, Syntax_Error);
if (Temp < 1) or (Temp > Num_Color) then
Syntax_Error := TRUE
else begin
ColorStr := TempStr;
Color := round(Temp)
end {else}
end {if}
end {if}
until not Syntax_Error
end {else}
end; {Select_Color}
{---------------- Get Scale ----------------
| |
| Get the scale for the X or Y axis. |
| |
| Called by: Get_Graph_Parameters_OK |
| |
| In parameter: Which axis ('X' or 'Y') |
| Out parameters: The scale in both numeric |
| and string representations |
| |
| TempStr, Temp, and Syntax_Error are used |
| as globals from calling procedure. |
---------------------------------------------}
procedure Get_Scale(Axis {in}: char;
var XYStr {out}: Str255;
var XY_Scale {out}: real);
var
I: integer; {Loop counter}
begin
if GEM_Interface then begin
Dialog := New_Dialog(9,0,0,11,20);
Prompt := Add_DItem(Dialog,G_Text,None,1,1,9,1,0,256*Black);
if Axis = 'X' then
Set_DText(Dialog,Prompt,'X-Scale',3,TE_Center)
else if Axis = 'Y' then
Set_DText(Dialog,Prompt,'Y-Scale',3,TE_Center)
else
Set_DText(Dialog,Prompt,'R-Scale',3,TE_Center);
for I := 1 to 8 do
Button[I] := Add_DItem(Dialog,G_Button,Selectable|Exit_Btn,
3,2*I+2,5,1,0,0);
Set_DText(Dialog, Button[1], '0.5',3,TE_Center);
Set_DText(Dialog, Button[2], '1',3,TE_Center);
Set_DText(Dialog, Button[3], '2',3,TE_Center);
Set_DText(Dialog, Button[4], '5',3,TE_Center);
Set_DText(Dialog, Button[5], '10',3,TE_Center);
Set_DText(Dialog, Button[6], '20',3,TE_Center);
Set_DText(Dialog, Button[7], '50',3,TE_Center);
Set_DText(Dialog, Button[8], '100',3,TE_Center);
Center_Dialog(Dialog);
Show_Mouse;
Pushed := Do_Dialog(Dialog,0);
End_Dialog(Dialog);
Hide_Mouse;
Out_Escape(ClrScr);
Delete_Dialog(Dialog);
if Pushed = Button[1] then XY_Scale := 0.5
else if Pushed = Button[2] then XY_Scale := 1.0
else if Pushed = Button[3] then XY_Scale := 2.0
else if Pushed = Button[4] then XY_Scale := 5.0
else if Pushed = Button[5] then XY_Scale := 10.0
else if Pushed = Button[6] then XY_Scale := 20.0
else if Pushed = Button[7] then XY_Scale := 50.0
else XY_Scale := 100.0
end {if}
else begin
repeat
Syntax_Error := FALSE;
writeln;
write(Axis,'-Scale <',XYStr,'>: ');readln(TempStr);
if TempStr <> '' then begin
for I:= 1 to length(TempStr) do
if not (TempStr[I] in ['.','0'..'9']) then
Syntax_Error := TRUE;
if not Syntax_Error then
Temp := Str_to_Num(TempStr, Syntax_Error);
if not Syntax_Error then
if Temp > 100 then begin
writeln('Enter a value <= 100');
Syntax_Error := TRUE
end {if}
else begin
XYStr := TempStr;
XY_Scale := Temp
end {else}
end {if}
until not Syntax_Error
end {else}
end; {Get_Scale}
{---- Get Graph parameters starts here ----}
begin
{ Get Grid-type or QUIT }
if GEM_Interface then begin
Dialog := New_Dialog(5,0,0,14,13);
Prompt := Add_DItem(Dialog,G_Text,None,1,1,12,1,0,256*Black);
Set_DText(Dialog,Prompt,'Grid?',3,TE_Center);
for I := 1 to 4 do
Button[I] := Add_DItem(Dialog,G_Button,Selectable|Exit_Btn,
4,2*I+1,6,1,0,0);
Set_DText(Dialog,Button[1],'RECT',3,TE_Center);
Set_DText(Dialog,Button[2],'TRIG',3,TE_Center);
Set_DText(Dialog,Button[3],'POLAR',3,TE_Center);
Set_DText(Dialog,Button[4],'QUIT',3,TE_Center);
Center_Dialog(Dialog);
Show_Mouse;
Pushed := Do_Dialog(Dialog,0);
End_Dialog(Dialog);
Hide_Mouse;
Out_Escape(ClrScr);
Delete_Dialog(Dialog);
if Pushed = Button[1] then TempStr := 'R'
else if Pushed = Button[2] then TempStr := 'T'
else if Pushed = Button[3] then TempStr := 'P'
else TempStr := 'Q'
end {if}
else begin
Out_Escape(Cursor_on);
Out_Escape(ClrScr);
writeln('Enter');
writeln;
writeln(' "R" for rectangular grid');
writeln;
writeln(' "P" for polar grid');
writeln;
writeln(' "T" for trigonometric grid');
writeln;
writeln(' "Q" for QUIT (or get new function)');
writeln;
writeln;
repeat
write('Grid type <',GridStr,'>: '); readln(TempStr);
if TempStr[1] in ['p','q','r','t'] then
TempStr[1] := chr(ord(TempStr[1])-32)
until (TempStr[1] in ['P', 'Q', 'R', 'T']) or (TempStr = '')
end; {else}
if TempStr = 'Q' then
Get_Graph_Parameters_OK := FALSE
else begin
if TempStr <> '' then
GridStr := TempStr;
Get_Graph_Parameters_OK := TRUE;
if GridStr = 'R' then
Grid := Rectangular
else if GridStr = 'P' then
Grid := Polar
else
Grid := Trigonometric;
{ Get grid scales }
if GEM_Interface then begin
if Grid = Rectangular then begin
Get_Scale('X', XStr, X_Scale);
Get_Scale('Y', YStr, Y_Scale)
end {if}
else if Grid = Polar then
Get_Scale('R', XStr, X_Scale)
else begin
X_Scale := Pi/2;
Get_Scale('Y', YStr, Y_Scale)
end {else}
end {if}
else begin
Out_Escape(ClrScr);
writeln('The origin is centered in the display');
write('area. You can adjust the ');
if Grid = Rectangular then begin
writeln('horizontal');
writeln('and vertical scales by entering the');
writeln('value corresponding to the first grid');
writeln('line. Integer values are recommended.');
writeln;
Get_Scale('X', XStr, X_Scale);
Get_Scale('Y', YStr, Y_Scale)
end {if}
else if Grid = Polar then begin
writeln('scale by');
writeln('entering the value of the radius of');
writeln('the first circle in the polar grid.');
writeln('Integer values are recommended.');
writeln;
Get_Scale('R', XStr, X_Scale)
end {else if}
else begin
writeln('vertical');
writeln('scale by entering the value of the');
writeln('first horizontal grid line. Integer');
writeln('values are recommended.');
writeln;
X_Scale := Pi/2;
XStr := '1.57079633';
Get_Scale('Y', YStr, Y_Scale)
end {else}
end; {else}
{ Get Graph Color }
if Res = Hi then
Color := Black
else
Select_Color;
{ Get Grid drawing option }
if GEM_Interface then begin
Dialog := New_Dialog(3,0,0,20,7);
for I := 1 to 3 do
Button[I] := Add_DItem(Dialog,G_Button,Selectable|Exit_Btn,
1,2*I-1,18,1,0,0);
Set_DText(Dialog,Button[1],'Clear / New Grid',3,TE_Center);
Set_DText(Dialog,Button[2],'Clear / No Grid',3,TE_Center);
Set_DText(Dialog,Button[3],'Draw on old Grid',3,TE_Center);
Center_Dialog(Dialog);
Show_Mouse;
Pushed := Do_Dialog(Dialog,0);
End_Dialog(Dialog);
Hide_Mouse;
Out_Escape(ClrScr);
Delete_Dialog(Dialog);
if Pushed = Button[1] then
Draw := Redraw
else if Pushed = Button[2] then
Draw := No_Grid
else
Draw := Old_Grid
end {if}
else begin
Out_Escape(ClrScr);
writeln('Choose one of the following:');
writeln(' 1. Clear screen / Draw new grid');
writeln(' 2. Clear screen / NO GRID');
writeln(' 3. Draw graph on previous screen');
writeln;
repeat
Syntax_Error := FALSE;
write('Which option <',DrawStr,'>: '); readln(TempStr);
until (TempStr[1] in ['1','2','3']) or (TempStr = '');
if TempStr <> '' then begin
DrawStr := TempStr;
if DrawStr = '1' then
Draw := Redraw
else if DrawStr = '2' then
Draw := No_Grid
else
Draw := Old_Grid
end; {if}
Out_Escape(Cursor_Off)
end {else}
end {else}
end; {Get_Graph_Parameters_OK}
{************* Number to String ***************
* *
* Converts the number parameter to string *
* representation. *
* *
* Called by: Rect_Grid, Polar_Grid *
* *
* In parameter: The number *
* Out parameter: The corresponding string *
************************************************}
procedure Num_to_Str(N {in}: real;
var NumStr {out}: Str7);
var
Integer_Part, {Integer part of number}
Fraction_Part: integer; {Fractional part of number}
TempI, {Temporary integer string}
TempF: Str7; {Temporary fraction part string}
begin
{ Find integer and fraction parts. }
if Abs(round(N) - N) < 0.01 then begin
Integer_Part := round(N);
Fraction_Part := 0
end {if}
else begin
Integer_Part := trunc(N);
Fraction_Part := round((N-Integer_Part)*100)
end; {else}
{ Convert integer part to string representation. }
if Integer_Part = 0 then
TempI := '0'
else
TempI := '';
while Integer_Part <> 0 do begin
TempI := concat(chr(ord('0') + Integer_Part MOD 10), TempI);
Integer_Part := Integer_Part DIV 10
end; {while}
{ Convert fraction part to string representation. }
TempF := '';
if Fraction_Part <> 0 then begin
while Fraction_Part <> 0 do begin
TempF := concat(chr(ord('0') + Fraction_Part MOD 10), TempF);
Fraction_Part := Fraction_Part DIV 10
end; {while}
TempF := concat('.', TempF)
end; {if}
NumStr := concat(TempI, TempF) { Concatenate integer and fraction parts. }
end; {Num_to_Str}
{************* Restore Screen *****************
* *
* Restore saved screen to display area. *
* *
* Called by: Rect_Graph, Polar_Graph *
* *
* In parameters: Pointer to physical screen, *
* Screen storage area *
************************************************}
procedure Restore_Screen(Display {in},
Old_Screen {in}: Screen_Ptr);
var
I: integer; {Loop control}
begin
{$P-}
for I := 0 to 31999 do
Display^[I] := Old_Screen^[I]
{$P+}
end; {Restore_Screen}
{*********** Draw Rectangular Graph ***********
* *
* Draws a graph in a rectangular coordinate *
* system. *
* *
* Called by: MAIN DRIVER *
* *
* In Parameters: Function being graphed, *
* Grid scales, Color of graph, *
* Grid drawing option *
* *
* Globals accessed: X_Center, Y_Center, X_Pix, *
* SF, Display_Area and *
* Temp_Screen *
************************************************}
procedure Rect_Graph(The_Function {in}: NodePtr;
X_Scale, Y_Scale {in}: real;
Graph_Color {in}: integer;
Draw {in}: Draw_Type);
var
SX: integer; {Loop counter and screen X-coordinate}
SY, {Screen Y-coordinate}
X, Y, {Logical X, Y coordinates}
XPix_per_Unit, {Pixels per horizontal grid unit}
YPix_per_Unit: real; {Pixels per vertical grid unit}
Dummy,
Undefined, {TRUE if function is undefined for given X}
Line_to_Flag: boolean; {True if last logical point was plotted on screen}
{----------- Draw Rectangular Grid -----------
| |
| Draws and labels a rectangular grid. |
| |
| Called by: Rect_Graph |
| |
| In parameter: Grid scales, grid type |
| |
| Global parameters accessed: Res, X_Pix, |
| Y_Pix, X_Center, Y_Center, SF |
-----------------------------------------------}
procedure Rect_Grid(X_Scale, Y_Scale {in}: real;
Grid_Option: Grid_Type);
const
Pi_code = 227; {Code for Pi character}
var
X, Y: integer; {Loop control}
SX1, SX2, SY1, SY2, {Screen coordinates}
Tick_Unit, {X-coordinate of first grid line to right of origin}
Pix_per_Unit: real; {Number of pixels in one grid unit}
NumStr: Str7; {String form of grid labels}
begin
Clear_Screen;
if Res = Hi then
Line_Color(Black)
else
Line_Color(Red);
Pix_per_Unit := X_Center/5;
{ Vertical grid lines }
SY1 := 0;
SY2 := Y_Pix - 1;
for X := -5 to 4 do begin
SX1 := X_Center + X*Pix_per_Unit;
Line(round(SX1),round(SY1),round(SX1),round(SY2))
end; {for}
SX1 := X_Pix - 1;
Line(round(SX1),round(SY1),round(SX1),round(SY2));
{ Horizontal grid lines }
SX1 := 0;
SX2 := X_Pix - 1;
for Y := -3 to 3 do begin
SY1 := Y_Center - Y*SF*Pix_per_Unit;
Line(round(SX1),round(SY1),round(SX2),round(SY1))
end; {for}
Line_Color(Black);
Line(X_Center,0,X_Center,Y_Pix-1);
Line(0,Y_Center,X_Pix-1,Y_Center);
{ X-axis labels }
Draw_Mode(2);
Tick_Unit := X_Scale;
SY1 := Y_Center + 0.3*SF*Pix_per_Unit;
if Grid = Rectangular then
for X := 1 to 4 do begin
Num_to_Str(X*Tick_Unit, NumStr);
SX1 := X_Center + X*Pix_per_Unit - 8*Length(NumStr)/2;
Draw_String(round(SX1),round(SY1),NumStr);
SX1 := X_Center - X*Pix_per_Unit - 8*(Length(NumStr)+1)/2;
NumStr := concat('-',NumStr);
Draw_String(round(SX1),round(SY1),NumStr)
end {for}
else begin { Trigonometric X-axis labels }
NumStr:= chr(Pi_code);
SX1 := X_Center + 2*Pix_per_Unit - 4;
Draw_String(round(SX1),round(SY1),NumStr);
SX1 := X_Center - 2*Pix_per_Unit - 8;
NumStr := concat('-', NumStr);
Draw_String(round(SX1),round(SY1), NumStr);
delete(NumStr,1,1);
NumStr := concat('2',NumStr);
SX1 := X_Center + 4*Pix_per_Unit - 8;
Draw_String(round(SX1),round(SY1),NumStr);
SX1 := X_Center - 4*Pix_per_Unit - 16;
NumStr := concat('-', NumStr);
Draw_String(round(SX1),round(SY1), NumStr);
delete(NumStr,1,2);
NumStr := concat(NumStr, '/2');
SX1 := X_Center + Pix_per_Unit - 12;
Draw_String(round(SX1),round(SY1),NumStr);
SX1 := X_Center - Pix_per_Unit - 16;
NumStr := concat('-', NumStr);
Draw_String(round(SX1),round(SY1), NumStr);
delete(NumStr,1,1);
NumStr := concat('3',NumStr);
SX1 := X_Center + 3*Pix_per_Unit - 16;
Draw_String(round(SX1),round(SY1),NumStr);
SX1 := X_Center - 3*Pix_per_Unit - 20;
NumStr := concat('-', NumStr);
Draw_String(round(SX1),round(SY1), NumStr)
end; {else}
{ Y-Axis labels }
Tick_Unit := Y_Scale;
SX1 := X_Center + 8;
for Y := 1 to 3 do begin
Num_to_Str(Y*Tick_Unit, NumStr);
SY1 := Y_Center - Y*SF*Pix_per_Unit + 4*(Y_Pix DIV 200);
Draw_String(round(SX1),round(SY1),NumStr);
SY1 := Y_Center + Y*SF*Pix_per_Unit + 4*(Y_Pix DIV 200);
NumStr := concat('-',NumStr);
Draw_String(round(SX1),round(SY1),NumStr)
end; {for}
Draw_Mode(1)
end; {Draw_RGrid}
{----- Rect_Graph begins here -----}
begin
if Draw = ReDraw then
Rect_Grid(X_Scale, Y_Scale, Grid)
else if Draw = Old_Grid then
Restore_Screen(Display_Area, Temp_Screen)
else
Clear_Screen;
Line_Color(Graph_Color);
XPix_per_Unit := (X_Center/5)/X_Scale;
YPix_per_Unit := SF*(X_Center/5)/Y_Scale;
Line_to_Flag := FALSE;
for SX := 0 to X_Pix - 1 do begin
X := (SX - X_Center)/XPix_per_Unit;
Y := Evaluate(The_Function, X, Dummy, Undefined);
if NOT Undefined then begin
SY := Y_Center - Y*YPix_per_Unit;
if Abs(SY) < 32000 then { it's safe to use round function }
if Line_to_Flag then
Line_to(SX, round(SY))
else begin
Plot(SX, round(SY));
Line_to_Flag := TRUE
end {else}
else
Line_to_Flag := FALSE
end {if}
else
Line_to_Flag := FALSE
end {for}
end; {Draw_RGraph}
{************* Draw Polar Graph ***************
* *
* Draws a graph using a polar coordinate *
* system. *
* *
* Called by: MAIN DRIVER *
* *
* In parameters: Function being graphed, *
* Grid scale, Color of graph, *
* Grid drawing option *
* *
* Globals accessed: X_Center, Y_Center, X_Pix, *
* SF, Display_Area and *
* Temp_Screen *
************************************************}
procedure Polar_Graph(The_Function {in}: NodePtr;
X_Scale {in}: real;
Graph_Color {in}: integer;
Draw {in}: Draw_Type);
var
SX, SY, {Screen coordinates}
Angle, {Angle in radians}
Radius, {Radius for given angle}
XPix_per_Unit, {Pixels per horizontal grid unit}
YPix_per_Unit: real; {Pixels per vertical grid unit}
A: integer; {Loop counter}
Dummy_flag,
Undefined, {TRUE if function is undefined for given angle}
Line_to_Flag: boolean; {TRUE if last logical point was plotted}
Dummy:char;
{-------------- Draw Polar Grid --------------
| |
| Draws and labels a Polar coordinate grid. |
| |
| Called by: Polar_Graph |
| |
| In_Parameter: Grid scale |
| |
| Global variables accessed: Res, X_Pix, |
| Y_Pix, X_Center, Y_Center, SF |
-----------------------------------------------}
procedure Polar_Grid(X_Scale {in}: real);
var
Pix_per_Unit: real;
R: integer;
A: integer;
Rad: real;
SX1, SY1, SX2, SY2: real;
Temp: real;
X,Y: real;
NumStr: Str7;
begin
Clear_Screen;
if Res = Hi then
Line_Color(Black)
else
Line_Color(Red);
{ Draw the concentric circles. }
Pix_per_Unit := X_Center/5;
for R := 1 to 4 do begin
Plot(X_Center+round(Pix_per_Unit*R), Y_Center);
for A := 1 to 72 do begin
Rad := A*Pi/36;
Temp := R*Cos(Rad)*Pix_per_Unit;
SX1 := X_Center + Temp;
Temp := R*Sin(Rad)*SF*Pix_per_Unit;
SY1 := Y_Center - Temp;
Line_to(round(SX1), round(SY1))
end {for}
end; {for}
{ Draw radiating lines. Lines at 0, 30, 60,... degrees go through the
origin. The rest start at the second circle. Otherwise the middle
of the grid gets too cluttered. }
for A := 0 to 35 do begin
Rad := A*Pi/36;
X := 4*Cos(Rad)*Pix_per_Unit;
Y := 4*Sin(Rad)*SF*Pix_per_Unit;
if A MOD 3 = 0 then begin
SX1 := X_Center + X;
SY1 := Y_Center - Y;
SX2 := X_Center - X;
SY2 := Y_Center + Y;
Line(round(SX1), round(SY1), round(SX2), round(SY2))
end {if}
else begin
SX1 := X_Center + X;
SY1 := Y_Center - Y;
SX2 := X_Center + X/2;
SY2 := Y_Center - Y/2;
Line(round(SX1), round(SY1), round(SX2), round(SY2));
SX1 := X_Center - X;
SY1 := Y_Center + Y;
SX2 := X_Center - X/2;
SY2 := Y_Center + Y/2;
Line(round(SX1), round(SY1), round(SX2), round(SY2))
end {else}
end; {for}
{ Draw X-axis labels. }
Draw_Mode(2);
SY1 := Y_Center + 0.3*SF*Pix_per_Unit;
for R := 1 to 4 do begin
Num_to_Str(R*X_Scale, NumStr);
SX1 := X_Center + R*Pix_per_Unit-8*Length(NumStr)/2;
Draw_String(round(SX1), round(SY1), NumStr)
end; {for}
{ Draw the angle labels. }
for A := 1 to 23 do begin
Rad := A*Pi/12;
if (A<6) or (A>18) then
SX1 := X_Center + 4.1*Cos(Rad)*Pix_per_Unit
else
SX1 := X_Center + 4.1*Cos(Rad)*Pix_per_Unit - 24;
Temp := Y_Pix DIV 200;
SY1 := Y_Center - 4*Sin(Rad)*(SF*Pix_per_Unit + Temp) + 4*Temp;
Num_to_Str(15*A, NumStr);
Draw_String(round(SX1), round(SY1), NumStr)
end; {for}
Draw_Mode(1)
end; {Draw_PGrid}
{----- Polar_Graph begins here -----}
begin
if Draw = ReDraw then
Polar_Grid(X_Scale)
else if Draw = Old_Grid then
Restore_Screen(Display_Area, Temp_Screen)
else
Clear_Screen;
Line_Color(Graph_Color);
XPix_per_Unit := (X_Center/5)/X_Scale;
YPix_per_Unit := SF*XPix_per_Unit;
Line_to_Flag := FALSE;
A := 0;
{ Since polar graphs don't have a fixed 'stopping' place, the program
will continue plotting until the user presses a key. The screen
display will remain until the user presses another key. }
repeat
Angle := A*Pi/180;
Radius := Evaluate(The_Function, Angle, Dummy_flag, Undefined);
if NOT Undefined then begin
SX := X_Center + Radius*Cos(Angle)*XPix_per_Unit;
SY := Y_Center - Radius*Sin(Angle)*YPix_Per_Unit;
if Abs(SY) < 32000 then
if Line_to_Flag then
Line_to(round(SX), round(SY))
else begin
Plot(round(SX), round(SY));
Line_to_Flag := TRUE
end {else}
else
Line_to_Flag := FALSE
end {if}
else
Line_to_Flag := FALSE;
A := A + 1;
if A > 32000 then begin
A := 0;
Line_to_Flag := FALSE
end {if}
until keypress;
read(Dummy)
end; {Draw_PGraph}
{*************** Save Screen ******************
* *
* Save screen display. *
* *
* Called by: MAIN DRIVER *
* *
* In parameter: Pointer to physical screen *
* Out parameter: Screen storage area *
************************************************}
procedure Save_Screen(Display {in}: Screen_Ptr;
var Temp_Screen {out}: Screen_Ptr);
var
I: integer; {Loop control}
begin
{$P-}
for I := 0 to 31999 do
Temp_Screen^[I] := Display^[I]
{$P+}
end; {Save_Screen}
{-----------------------------------------
M A I N D R I V E R
-----------------------------------------}
begin
if Init_Gem >= 0 then begin
Hide_Mouse;
Initialization;
Get_Expression(Infix, PostFix);
while InFix <> 'Q' do begin
{ Get the parameter values desired by the user. }
while Get_Graph_Parameters_OK(Grid,X_Scale,Y_Scale,Color,Draw) do begin
if (Grid = Rectangular) or (Grid = Trigonometric) then
Rect_Graph(PostFix,X_Scale,Y_Scale,Color,Draw)
else
Polar_Graph(PostFix,X_Scale,Color,Draw);
{ Freeze display until user presses a key. }
repeat
Event := Get_Event(E_Button|E_Keyboard,1,1,1,0,FALSE,0,0,0,0,
FALSE,0,0,0,0,DummyMsg,D,D,D,D,D,D);
until (Event = E_Button) or (Event = E_Keyboard);
{ Save the screen display. }
Save_Screen(Display_Area, Temp_Screen)
end; {while}
{ Return postfix storage to the available memory heap. }
while PostFix <> NIL do begin
TempPtr := PostFix;
PostFix := PostFix^.Link;
dispose(TempPtr)
end; {while}
{ Get ready to do it again. }
Get_Expression(Infix, PostFix)
end; {while}
Show_Mouse;
Exit_Gem
end {if}
end.